use std::{net::SocketAddr, sync::Arc, thread, time::Duration};

use chardev::{
    telnet::{chardevice_telnet_map, CharDeviceTelnet},
    CharBackendDevice, CharBackendMap, CharDevice, CharDeviceSignal, CharPollStatus,
};
use dfs::{inode::link_path_walk, seq_file::SeqFileCreateOptions};
use manage::{ManageSiminkStatus, SiminkManage};
use monitor::Monitor;
use objectid::ObjectId;
use rcu::{rcu_register_thread, rcu_unregister_thread};

use super::{chardevice_mon::CharDeviceMon, MonitorFile};

fn register_notify(
    sockaddr: SocketAddr,
    char_backend_map: Arc<CharBackendMap>,
    mon: Arc<Monitor>,
    manage: Arc<SiminkManage>,
) {
    let mon_inode = link_path_walk("/monitor").unwrap();
    SeqFileCreateOptions::new()
        .read_write(true)
        .name(sockaddr.to_string().as_str())
        .create_seq_file(mon_inode, Arc::new(MonitorFile { map: char_backend_map.clone() }))
        .unwrap();

    manage.clone().push_worker(
        thread::Builder::new()
            .name(format!("telnet[{}]", sockaddr))
            .spawn(move || {
                rcu_register_thread();

                let char_telnet = Arc::new(CharDeviceTelnet::create(sockaddr));
                let cm = Arc::new(CharDeviceMon::create(
                    mon,
                    Arc::new(CharBackendDevice::create(char_telnet.clone())),
                ));
                char_backend_map.register_char_backend(cm.clone());
                char_backend_map.select_current(cm.id());

                let mut char_device =
                    CharDevice::create(char_telnet.clone(), char_backend_map.clone());
                char_backend_map.register_char_device_impl(char_telnet);
                char_device.set_monitor(cm.id());
                char_device.prepare().unwrap();
                'out: loop {
                    if char_device.wait(Duration::from_millis(100)).unwrap() {
                        'inner: loop {
                            let status = char_device.poll(Duration::from_millis(100)).unwrap();
                            match status {
                                CharPollStatus::Ready => {
                                    let signal = char_device.handle().unwrap();
                                    match signal {
                                        CharDeviceSignal::None | CharDeviceSignal::ReqExit => {},
                                        CharDeviceSignal::ReqSwitch => {
                                            char_device.switch_to_monitor().unwrap();
                                        },
                                    }
                                },
                                CharPollStatus::Timeout => {},
                                CharPollStatus::ConnectAborted => {
                                    break 'inner;
                                },
                            }
                            char_device.update_current();
                            if manage.simink_status() == ManageSiminkStatus::Quiting {
                                char_device.end(status).unwrap();
                                break 'out;
                            }
                        }
                    }
                    if manage.simink_status() == ManageSiminkStatus::Quiting {
                        char_device.end(CharPollStatus::Timeout).unwrap();
                        break 'out;
                    }
                }
                rcu_unregister_thread();
            })
            .unwrap(),
    );
}

pub(crate) fn telnet_init(mon: Arc<Monitor>, manage: Arc<SiminkManage>) {
    let telnet_map = chardevice_telnet_map();
    telnet_map.register_notify(register_notify, mon, manage);
}
